<template>
    <!-- 时间选择区域 -->

    <div class="time-echarts">
        <component :is="portalComponent" to="body" v-if="hasPortalSupport">
            <!-- 原有的选中 tip -->
            <div
                v-if="selectedTimePlan"
                class="time-tip"
                :style="{
                    left: `${tipPosition.x}px`,
                    top: `${tipPosition.y}px`,
                    transform: 'translate(-50%, -100%)',
                    position: 'fixed',
                    zIndex: tipZIndex,
                }"
            >
                {{ tipContent }}
            </div>

            <!-- 新增的预览 tip -->
            <div
                v-show="previewTipVisible"
                class="time-tip preview-tip"
                :style="{
                    left: `${previewTipPosition.x}px`,
                    top: `${previewTipPosition.y}px`,
                    transform: 'translate(-50%, -100%)',
                    position: 'fixed',
                    zIndex: tipZIndex,
                }"
            >
                {{ previewTipContent }}
            </div>
        </component>

        <!-- 不支持传送门的回退方案 -->
        <div class="tip-container" v-else>
            <!-- 原有的选中 tip -->
            <div
                v-if="selectedTimePlan"
                class="time-tip"
                :style="{
                    left: `${tipPosition.x}px`,
                    top: `${tipPosition.y}px`,
                    transform: 'translate(-50%, -100%)',
                    position: 'fixed',
                    zIndex: tipZIndex,
                }"
            >
                {{ tipContent }}
            </div>

            <!-- 新增的预览 tip -->
            <div
                v-show="previewTipVisible"
                class="time-tip preview-tip"
                :style="{
                    left: `${previewTipPosition.x}px`,
                    top: `${previewTipPosition.y}px`,
                    transform: 'translate(-50%, -100%)',
                    position: 'fixed',
                    zIndex: tipZIndex,
                }"
            >
                {{ previewTipContent }}
            </div>
        </div>

        <div class="contents-week">
            <div class="bottom">
                <div class="leftbar">
                    <div
                        v-for="day in weekDays"
                        :key="day.value"
                        class="weekday-week"
                        :style="weekdayStyle"
                    >
                        {{ day.label }}
                    </div>
                </div>
                <div class="rightweek" ref="rightWeekRef" :style="rightWeekStyle">
                    <div class="grid-container">
                        <div v-for="i in 168" :key="i" class="grid-column"></div>
                    </div>
                    <canvas
                        ref="canvasRef"
                        :style="{
                            cursor: dragState.isDragging ? 'col-resize' : 'pointer',
                            pointerEvents: 'auto',
                        }"
                    ></canvas>
                </div>
            </div>
            <div class="bottomhour-week">
                <ul class="ul-week">
                    <li
                        v-for="hour in hours"
                        :key="hour"
                        :style="{ width: `${smallestUnitWeek}px` }"
                    >
                        {{ hour }}
                    </li>
                </ul>
            </div>
        </div>
    </div>
</template>

<script lang="ts" setup>
import { isVue2 } from 'vue-demi';
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue-demi';

// 类型定义
interface TimeSegment {
    x: number;
    y: number;
    width: number;
}

interface DragState {
    isDragging: boolean;
    startX: number;
    startY: number;
    startRow: number;
    hasMoved: boolean;
    isMovingBlock: boolean;
    movingStartX: number;
    movingStartY: number;
    selectedBlockStartX: number;
    selectedBlockWidth: number;
    selectedBlockStartRow: number;
    canMoveBlock: boolean;
    isResizing: boolean;
    resizeDirection: 'left' | 'right' | null;
    clickedPlan: { startDate: string; endDate: string } | null;
}

// 常量定义
const HOURS_IN_DAY = 24;
const MINUTES_PER_HOUR = 60;
const MINUTES_PER_GRID = 15;
const MAX_BLOCK_HEIGHT = 40;

const weekDays = [
    { label: '周一', value: '1' },
    { label: '周二', value: '2' },
    { label: '周三', value: '3' },
    { label: '周四', value: '4' },
    { label: '周五', value: '5' },
    { label: '周六', value: '6' },
    { label: '周日', value: '7' },
];

// Props 定义
const props = defineProps<{
    timePlan: Array<{ startDate: string; endDate?: string }>;
}>();

const emit = defineEmits(['timeSelect']);

// 状态管理
const rightWeekRef = ref<HTMLDivElement | null>(null);
const canvasRef = ref<HTMLCanvasElement | null>(null);
const hours = ref<number[]>(Array.from({ length: HOURS_IN_DAY }, (_, i) => i));
const smallestUnitWeek = ref(0);
const timeToPixels = ref(0);

// 维护本地的时间计划数组
const localTimePlan = ref<Array<{ startDate: string; endDate: string }>>([]);

// 添加状态来跟踪当前选中的时间块
const selectedTimePlan = ref<{ startDate: string; endDate: string } | null>(null);

// 添加 tip 相关的响应式数据
const tipContent = ref('');
const tipPosition = ref({ x: 0, y: 0 });
// 添加动态 z-index 值
const tipZIndex = ref(1000);

// 添加预览相关的状态，用于显示拖拽时的预览提示
const previewTipVisible = ref(false);
const previewTipContent = ref('');
const previewTipPosition = ref({ x: 0, y: 0 });

// Portal 组件检测
const portalComponent = ref('div');
const hasPortalSupport = ref(false);

// 添加 IntersectionObserver 相关变量
let timeBlockObserver: IntersectionObserver | null = null;
const isSelectedBlockVisible = ref(false);

// 添加清理函数数组
const trackingCleanupFunctions = ref<Array<() => void>>([]);

// 获取页面最大 z-index 值的函数
const getMaxZIndex = (): number => {
    let maxZIndex = 0;
    const allElements = document.getElementsByTagName('*');
    for (let i = 0; i < allElements.length; i++) {
        const element = allElements[i];
        const style = window.getComputedStyle(element);
        const zIndex = parseInt(style.zIndex, 10);
        if (!isNaN(zIndex) && zIndex > maxZIndex) {
            maxZIndex = zIndex;
        }
    }
    return maxZIndex;
};

// 更新 tip 的 z-index 值
const updateTipZIndex = () => {
    tipZIndex.value = getMaxZIndex() + 1;
};

// 添加一个工具函数来获取周几的标签
const getDayLabel = (day: string): string => {
    const dayIndex = parseInt(day) - 1;
    return weekDays[dayIndex]?.label || '';
};

// 添加一个局部变量来保存滚动定时器ID
let scrollTimerId: number | null = null;

// 添加滚动事件处理函数
const handleScroll = () => {
    // 只有当有选中的时间块时才需要处理
    if (selectedTimePlan.value) {
        // 检查选中的时间块是否可见
        if (isSelectedBlockVisible.value) {
            // 如果可见，更新 tip 位置
            updateTip();
        }

        // 滚动时重新触发观察器，确保可见性状态正确
        if (scrollTimerId) {
            clearTimeout(scrollTimerId);
        }

        scrollTimerId = window.setTimeout(() => {
            // 重新设置观察器，确保可见性检查准确
            observeSelectedTimeBlock();
        }, 100);
    }
};

// 修改 updateTip 函数，确保正确计算位置
const updateTip = () => {
    if (!selectedTimePlan.value) {
        return;
    }

    // 解析开始和结束时间
    const startTime = parseTime(selectedTimePlan.value.startDate);
    const endTime = parseTime(selectedTimePlan.value.endDate);
    if (!startTime || !endTime) {
        return;
    }

    // 使用 getDayLabel 函数获取周几标签
    const startDayLabel = getDayLabel(startTime.day);
    const endDayLabel = getDayLabel(endTime.day);

    // 设置 tip 内容，使用解析后的时间
    tipContent.value = `${startDayLabel}-${String(startTime.hours).padStart(2, '0')}:${String(startTime.minutes).padStart(2, '0')} - ${endDayLabel}-${String(endTime.hours).padStart(2, '0')}:${String(endTime.minutes).padStart(2, '0')}`;

    // 确保在下一帧绘制完成后再计算位置
    requestAnimationFrame(() => {
        const canvas = canvasRef.value;
        if (!canvas) {
            return;
        }

        // 获取最新的画布位置信息，包括滚动位置
        const canvasRect = canvas.getBoundingClientRect();

        // 计算时间块的位置
        const gridWidth = canvas.width / 24;
        const rowHeight = canvas.height / 7;

        // 使用解析后的时间计算位置
        const startTotalMinutes =
            (parseInt(startTime.day) - 1) * 24 * 60 + startTime.hours * 60 + startTime.minutes;

        const endTotalMinutes =
            (parseInt(endTime.day) - 1) * 24 * 60 + endTime.hours * 60 + endTime.minutes;

        // 计算中间点的分钟数
        const middleTotalMinutes = (startTotalMinutes + endTotalMinutes) / 2;

        // 将中间点的分钟数转换回天和小时
        const middleDay = Math.floor(middleTotalMinutes / (24 * 60));
        const middleHourMinutes = middleTotalMinutes % (24 * 60);
        const middleHour = middleHourMinutes / 60;

        // 计算中间点的位置
        const middleX = (middleHour % 24) * gridWidth;
        const middleY = middleDay * rowHeight;

        // 设置 tip 位置，使用窗口绝对位置（考虑最新的滚动位置）
        tipPosition.value = {
            x: canvasRect.left + middleX,
            y: canvasRect.top + middleY - 10,
        };

        // 更新 tip 的 z-index 值，确保它拥有最高层级
        updateTipZIndex();

        // 不需要设置 tipVisible.value，因为我们使用 selectedTimePlan 控制 tip 显示
    });
};

// 添加对 props.timePlan 的监听
watch(
    () => props.timePlan,
    (newValue) => {
        if (Array.isArray(newValue)) {
            // 更新本地数据
            localTimePlan.value = [...newValue];

            // 如果当前选中的时间段已被删除，清除选中状态和tip
            if (selectedTimePlan.value) {
                // 改进比较逻辑：检查在新数组中是否有匹配当前选中项的时间段
                const stillExists = newValue.some((plan) => {
                    // 检查 startDate 和 endDate 值是否相同，而不是引用相等
                    return (
                        plan.startDate === selectedTimePlan.value?.startDate &&
                        plan.endDate === selectedTimePlan.value?.endDate
                    );
                });

                if (!stillExists) {
                    // 只有当选中的时间段真的被删除时，才清除选中状态
                    selectedTimePlan.value = null;
                    dragState.value.canMoveBlock = false;
                } else {
                    // 如果选中的时间段仍然存在，用新数组中的对应项更新选中的时间段
                    // 这确保保持引用最新
                    const updatedPlan = newValue.find(
                        (plan) =>
                            plan.startDate === selectedTimePlan.value?.startDate &&
                            plan.endDate === selectedTimePlan.value?.endDate,
                    );

                    if (updatedPlan) {
                        selectedTimePlan.value = updatedPlan;
                        // 保持选中状态，确保 tip 仍然可见
                        updateTip();
                    }
                }
            }

            // 重新绘制时间块
            nextTick(() => {
                drawTimeBlocks();
            });
        }
    },
    { deep: true, immediate: true },
);

const dragState = ref<DragState>({
    isDragging: false,
    startX: 0,
    startY: 0,
    startRow: 0,
    hasMoved: false,
    isMovingBlock: false,
    movingStartX: 0,
    movingStartY: 0,
    selectedBlockStartX: 0,
    selectedBlockWidth: 0,
    selectedBlockStartRow: 0,
    canMoveBlock: false,
    isResizing: false,
    resizeDirection: null,
    clickedPlan: null,
});

// 添加圆角矩形绘制函数
const drawRoundedRect = (
    ctx: CanvasRenderingContext2D,
    x: number,
    y: number,
    width: number,
    height: number,
    radius: number,
) => {
    ctx.beginPath();
    ctx.moveTo(x + radius, y);
    ctx.lineTo(x + width - radius, y);
    ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
    ctx.lineTo(x + width, y + height - radius);
    ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
    ctx.lineTo(x + radius, y + height);
    ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
    ctx.lineTo(x, y + radius);
    ctx.quadraticCurveTo(x, y, x + radius, y);
    ctx.closePath();
};

// 添加函数：检查点击是否在某个时间块内
const findClickedTimePlan = (
    x: number,
    y: number,
): { startDate: string; endDate: string } | null => {
    const gridWidth = canvasRef.value?.width ? canvasRef.value.width / 24 : 0;
    const rowHeight = canvasRef.value?.height ? canvasRef.value.height / 7 : 0;

    // 首先检查当前选中的时间块
    if (selectedTimePlan.value) {
        const startTime = parseTime(selectedTimePlan.value.startDate);
        const endTime = parseTime(selectedTimePlan.value.endDate);
        if (startTime && endTime) {
            const startRow = parseInt(startTime.day) - 1;
            const endRow = parseInt(endTime.day) - 1;
            const clickedRow = Math.floor(y / rowHeight);

            // 检查是否在正确的行范围内
            if (clickedRow >= startRow && clickedRow <= endRow) {
                // 计算点击位置对应的小时
                const clickHour = x / gridWidth;

                // 根据所在行判断时间范围
                if (clickedRow === startRow && clickedRow === endRow) {
                    // 同一天的情况
                    const startX = startTime.hours + startTime.minutes / 60;
                    const endX = endTime.hours + endTime.minutes / 60;

                    // 适当扩大点击区域以便更容易选中
                    const isInRange =
                        clickHour >= (startX * gridWidth - 8) / gridWidth &&
                        clickHour <= (endX * gridWidth + 8) / gridWidth;

                    if (isInRange) {
                        return selectedTimePlan.value;
                    }
                } else if (clickedRow === startRow) {
                    // 开始行
                    const startX = startTime.hours + startTime.minutes / 60;

                    // 适当扩大起始点击区域
                    if (clickHour >= (startX * gridWidth - 8) / gridWidth && clickHour <= 24) {
                        return selectedTimePlan.value;
                    }
                } else if (clickedRow === endRow) {
                    // 结束行
                    const endX = endTime.hours + endTime.minutes / 60;

                    // 适当扩大结束点击区域
                    if (clickHour >= 0 && clickHour <= (endX * gridWidth + 8) / gridWidth) {
                        return selectedTimePlan.value;
                    }
                } else {
                    // 中间行，整行都是时间段的范围
                    if (clickHour >= 0 && clickHour <= 24) {
                        return selectedTimePlan.value;
                    }
                }
            }
        }
    }

    // 如果没有点击在选中的时间块上，则检查其他时间块
    // 反转数组以便从最后添加的时间块开始检查
    return (
        [...localTimePlan.value].reverse().find((plan) => {
            // 如果是已选中的时间块，跳过（因为已经检查过了）
            if (
                selectedTimePlan.value &&
                plan.startDate === selectedTimePlan.value.startDate &&
                plan.endDate === selectedTimePlan.value.endDate
            ) {
                return false;
            }

            const startTime = parseTime(plan.startDate);
            const endTime = parseTime(plan.endDate);
            if (!startTime || !endTime) return false;

            const startRow = parseInt(startTime.day) - 1;
            const endRow = parseInt(endTime.day) - 1;
            const clickedRow = Math.floor(y / rowHeight);

            // 检查是否在正确的行范围内
            if (clickedRow < startRow || clickedRow > endRow) return false;

            // 计算点击位置对应的小时
            const clickHour = x / gridWidth;

            // 根据所在行判断时间范围
            if (clickedRow === startRow && clickedRow === endRow) {
                // 同一天的情况
                const startX = startTime.hours + startTime.minutes / 60;
                const endX = endTime.hours + endTime.minutes / 60;

                // 判断是否点击在时间块上，严格限制在时间块范围内
                const isInRange =
                    clickHour >= (startX * gridWidth - 5) / gridWidth &&
                    clickHour <= (endX * gridWidth + 5) / gridWidth;
                return isInRange;
            } else if (clickedRow === startRow) {
                // 开始行
                const startX = startTime.hours + startTime.minutes / 60;

                // 严格限制：必须点击在时间块本身上，而不是整行
                // 如果开始时间不是00:00，则检查点击位置是否在开始时间之后
                if (startTime.hours !== 0 || startTime.minutes !== 0) {
                    return clickHour >= (startX * gridWidth - 5) / gridWidth;
                } else {
                    // 如果开始时间是00:00，则更严格地限制在时间块范围内
                    // 需要检查是否在24小时范围内
                    return clickHour >= 0 && clickHour <= 24;
                }
            } else if (clickedRow === endRow) {
                // 结束行
                const endX = endTime.hours + endTime.minutes / 60;

                // 严格限制：必须点击在时间块本身上，而不是整行
                // 如果结束时间不是23:59，则检查点击位置是否在结束时间之前
                if (endTime.hours !== 23 || endTime.minutes !== 59) {
                    return clickHour <= (endX * gridWidth + 5) / gridWidth;
                } else {
                    // 如果结束时间是23:59，则更严格地限制在时间块范围内
                    // 需要检查是否在24小时范围内
                    return clickHour >= 0 && clickHour <= 24;
                }
            } else {
                // 中间行，整行都是时间段的范围，但也要限制在时间块范围内
                return clickHour >= 0 && clickHour <= 24;
            }
        }) || null
    );
};

// 修改 checkResizeZone 函数，确保包含边界点
const checkResizeZone = (
    x: number,
    plan: { startDate: string; endDate: string },
    row?: number,
): 'left' | 'right' | null => {
    const gridWidth = canvasRef.value?.width ? canvasRef.value.width / 24 : 0;
    const startTime = parseTime(plan.startDate);
    const endTime = parseTime(plan.endDate);
    if (!startTime || !endTime) return null;

    // 计算时间块的开始和结束位置
    const startX = (startTime.hours + startTime.minutes / 60) * gridWidth;
    const endX = (endTime.hours + endTime.minutes / 60) * gridWidth;

    // 计算时间块的总宽度
    const blockWidth = endX - startX;

    // 根据时间块宽度动态计算合适的边缘检测区域
    // 对于短时间块（如15分钟），使用更小的边缘区域
    // 对于较长时间块，保持原有的固定区域大小
    // 确保边缘区域最小为1.5像素，最大为3像素，且不超过时间块宽度的25%
    let resizeZone = Math.min(3, Math.max(1.5, blockWidth * 0.25));

    // 15分钟时间块的特殊处理
    // 检查是否是15分钟时间块（通常宽度较小）
    const minutes15Width = gridWidth / 4; // 15分钟在网格中的宽度
    if (Math.abs(blockWidth - minutes15Width) < 1) {
        // 允许1像素的误差
        // 对于15分钟的时间块，使用更大的边缘区域，确保用户能更容易地调整
        resizeZone = Math.max(3, blockWidth * 0.4); // 增大边缘检测区域
    }

    // 如果提供了行信息，检查是否在正确的行上
    if (row !== undefined) {
        const startRow = parseInt(startTime.day) - 1;
        const endRow = parseInt(endTime.day) - 1;

        // 只在对应的行检查边缘
        if (row === startRow) {
            // 修改左侧检测逻辑：包含起始位置和向外延伸的区域
            if (Math.abs(x - startX) <= resizeZone) return 'left';
        }

        if (row === endRow) {
            // 修改右侧检测逻辑：包含结束位置和向外延伸的区域
            if (Math.abs(x - endX) <= resizeZone) return 'right';
        }
    } else {
        // 如果没有提供行信息，检查所有可能的边缘
        // 修改左侧检测逻辑：包含起始位置和向外延伸的区域
        if (Math.abs(x - startX) <= resizeZone) return 'left';
        // 修改右侧检测逻辑：包含结束位置和向外延伸的区域
        if (Math.abs(x - endX) <= resizeZone) return 'right';
    }

    return null;
};

// 添加一个重置tip位置的函数
const resetTipPosition = () => {
    // 重置tip位置到默认值
    tipPosition.value = { x: 0, y: 0 };
    tipContent.value = '';
};

// 修改 IntersectionObserver 的回调函数，在取消选中时重置tip位置
const observeSelectedTimeBlock = () => {
    // 断开之前的可见性观察器
    if (timeBlockObserver) {
        timeBlockObserver.disconnect();
        timeBlockObserver = null;
    }

    // 清理可能存在的追踪元素
    cleanupTrackingElements();

    // 如果没有选中时间块，不需要观察
    if (!selectedTimePlan.value) {
        isSelectedBlockVisible.value = false;
        return;
    }

    // 确保画布容器存在
    const container = canvasRef.value?.parentElement;
    if (!container) {
        return;
    }

    // 设置默认为可见
    isSelectedBlockVisible.value = true;

    // 计算选中时间块的位置
    const startTime = parseTime(selectedTimePlan.value.startDate);
    const endTime = parseTime(selectedTimePlan.value.endDate);
    if (!startTime || !endTime) {
        return;
    }

    // 创建一个临时 div 来跟踪选中时间块的位置
    const tempDiv = document.createElement('div');
    const canvas = canvasRef.value;

    if (canvas) {
        const gridWidth = canvas.width / 24;
        const rowHeight = canvas.height / 7;

        // 计算时间块的位置
        const startDay = parseInt(startTime.day) - 1;
        const startHour = startTime.hours;
        const endDay = parseInt(endTime.day) - 1;
        const endHour = endTime.hours + (endTime.minutes > 0 ? 1 : 0);

        // 设置临时div的样式，使其覆盖时间块的区域
        tempDiv.style.position = 'absolute';

        // 计算时间块的宽度（小时为单位）
        const blockWidthInHours =
            endDay === startDay
                ? Math.abs(endHour - startHour) // 使用绝对值确保宽度始终为正
                : 24 - startHour;

        // 为短时间块（特别是15分钟的小时间块）提供额外的边距
        // 为短时间块增加额外的边距，确保更好地检测可见性
        const isShortTimeBlock = blockWidthInHours <= 0.5; // 30分钟或更短
        const extraMargin = isShortTimeBlock ? 10 : 5;

        tempDiv.style.left = `${Math.max(0, startHour * gridWidth - extraMargin)}px`;
        tempDiv.style.top = `${Math.max(0, startDay * rowHeight - extraMargin)}px`;
        tempDiv.style.width = `${blockWidthInHours * gridWidth + extraMargin * 2}px`;
        tempDiv.style.height = `${rowHeight + extraMargin * 2}px`;
        tempDiv.style.visibility = 'hidden';

        // 添加调试边框（仅在开发环境中）
        if (process.env.NODE_ENV === 'development') {
            tempDiv.style.border = '1px dashed rgba(255, 0, 0, 0.3)';
            tempDiv.style.visibility = 'visible'; // 在开发环境中可见用于调试
            tempDiv.style.opacity = '0.3';
            tempDiv.style.pointerEvents = 'none'; // 确保它不会干扰用户交互
        }

        tempDiv.setAttribute('data-time-block-tracker', 'true');

        // 添加到容器
        container.appendChild(tempDiv);

        // 获取可能的滚动容器
        let scrollContainer = container;
        while (scrollContainer) {
            const style = window.getComputedStyle(scrollContainer);
            if (
                ['auto', 'scroll'].includes(style.overflowY) ||
                ['auto', 'scroll'].includes(style.overflow)
            ) {
                break;
            }
            scrollContainer = scrollContainer.parentElement;
            if (!scrollContainer) break;
        }

        // 创建 IntersectionObserver 来观察临时 div 的可见性
        timeBlockObserver = new IntersectionObserver(
            (entries) => {
                // 检查时间块是否在视口内
                // 只有当时间块完全不可见（intersectionRatio 为 0）时才认为不可见
                const entry = entries[0];
                const isCompletelyInvisible = entry.intersectionRatio === 0;

                // 更新可见性状态
                isSelectedBlockVisible.value = !isCompletelyInvisible;

                // 只有在时间块完全不可见时，才取消选中状态
                if (isCompletelyInvisible && selectedTimePlan.value) {
                    resetTipPosition();

                    // 取消选中状态
                    selectedTimePlan.value = null;
                    dragState.value.canMoveBlock = false;

                    // 重绘时间块
                    drawTimeBlocks();
                }
            },
            {
                root: scrollContainer || null,
                // 使用数组阈值，便于精确判断完全不可见的状态
                threshold: [0, 0.01, 0.1, 0.5, 1],
                // 减小边距，避免误判
                rootMargin: '0px',
            },
        );

        // 开始观察临时 div
        timeBlockObserver.observe(tempDiv);

        // 将清理函数添加到数组中，以便在组件销毁时清理
        const cleanupTracker = () => {
            try {
                // 先断开观察器
                if (timeBlockObserver) {
                    timeBlockObserver.unobserve(tempDiv);
                    timeBlockObserver.disconnect();
                }

                // 再移除DOM元素
                if (tempDiv.parentElement) {
                    tempDiv.parentElement.removeChild(tempDiv);
                }
            } catch (error) {
                console.error('Error cleaning up tracking elements:', error);
            }
        };
        trackingCleanupFunctions.value.push(cleanupTracker);
    }
};

// 修改handleMouseDown函数，确保在选中时立即计算正确的tip位置
const handleMouseDown = (e: MouseEvent) => {
    // 添加 user-select: none
    document.body.style.userSelect = 'none';

    const canvas = canvasRef.value;
    const rect = canvas?.getBoundingClientRect();
    if (!canvas || !rect) return;

    const clickX = e.clientX - rect.left;
    const clickY = e.clientY - rect.top;
    const clickedPlan = findClickedTimePlan(clickX, clickY);
    const clickedRow = Math.floor(clickY / (canvas.height / 7));

    // 如果点击的是已选中的时间块，处理移动和调整大小
    if (
        clickedPlan &&
        selectedTimePlan.value &&
        clickedPlan.startDate === selectedTimePlan.value.startDate &&
        clickedPlan.endDate === selectedTimePlan.value.endDate
    ) {
        // 检查是否点击在边缘调整区域，传入行信息
        const resizeDirection = checkResizeZone(clickX, clickedPlan, clickedRow);
        if (resizeDirection) {
            dragState.value.isResizing = true;
            dragState.value.resizeDirection = resizeDirection;
            // 确保更新观察者和tip位置
            requestAnimationFrame(() => {
                observeSelectedTimeBlock();
                updateTip();
            });
            return;
        }

        // 设置移动状态
        dragState.value.isMovingBlock = true;
        dragState.value.movingStartX = clickX;
        dragState.value.movingStartY = clickY;
        dragState.value.canMoveBlock = true;

        const startTime = parseTime(clickedPlan.startDate);
        const endTime = parseTime(clickedPlan.endDate);
        if (startTime && endTime) {
            const gridWidth = canvas.width / 24;
            dragState.value.selectedBlockStartX =
                (startTime.hours + startTime.minutes / 60) * gridWidth;
            dragState.value.selectedBlockWidth =
                (endTime.hours +
                    endTime.minutes / 60 -
                    (startTime.hours + startTime.minutes / 60)) *
                gridWidth;
            dragState.value.selectedBlockStartRow = parseInt(startTime.day) - 1;
        }

        // 确保更新观察者和tip位置
        requestAnimationFrame(() => {
            observeSelectedTimeBlock();
            updateTip();
        });
        return;
    }

    // 记录点击的时间块，但不立即选中
    dragState.value.clickedPlan = clickedPlan;

    // 设置拖拽状态
    dragState.value.isDragging = true;
    dragState.value.startX = clickX;
    dragState.value.startY = clickY;
    dragState.value.startRow = Math.floor(clickY / (canvas.height / 7));
    dragState.value.hasMoved = false;
};

const handleMouseMove = (e: MouseEvent) => {
    const canvas = canvasRef.value;
    const rect = canvas?.getBoundingClientRect();
    if (!canvas || !rect) return;

    const mouseX = e.clientX - rect.left;
    const mouseY = e.clientY - rect.top;
    const clickedRow = Math.floor(mouseY / (canvas.height / 7));

    // 检查时间块的可见性
    // 只在进行交互性操作的时候检查可见性，以减少不必要的计算
    if (selectedTimePlan.value && (dragState.value.isResizing || dragState.value.isMovingBlock)) {
        // 在交互过程中重新设置观察器
        observeSelectedTimeBlock();

        // 如果时间块已经变为不可见状态，立即取消操作
        if (!isSelectedBlockVisible.value) {
            // 取消操作状态
            dragState.value.isResizing = false;
            dragState.value.resizeDirection = null;
            dragState.value.isMovingBlock = false;
            dragState.value.canMoveBlock = false;
            // 重置选中状态
            resetTipPosition();
            selectedTimePlan.value = null;
            // 重绘时间块
            drawTimeBlocks();
            return;
        }
    }

    // 更新鼠标样式
    if (selectedTimePlan.value && !dragState.value.isMovingBlock && !dragState.value.isResizing) {
        // 传入行信息
        const resizeDirection = checkResizeZone(mouseX, selectedTimePlan.value, clickedRow);
        canvas.style.cursor = resizeDirection ? 'ew-resize' : 'pointer';
    } else if (
        !dragState.value.isDragging &&
        !dragState.value.isMovingBlock &&
        !dragState.value.isResizing
    ) {
        // 当没有选中时间块且不在拖拽/移动/调整大小状态时，确保光标为指针样式
        canvas.style.cursor = 'pointer';
    }

    // 处理时间块的调整大小
    if (dragState.value.isResizing && selectedTimePlan.value) {
        const gridWidth = canvas.width / 24;
        const quarterWidth = gridWidth / 4;

        // 解析当前选中时间块的开始和结束时间
        const startTime = parseTime(selectedTimePlan.value.startDate);
        const endTime = parseTime(selectedTimePlan.value.endDate);
        if (!startTime || !endTime) return;

        // 计算新的时间位置
        if (dragState.value.resizeDirection === 'left') {
            // 调整开始时间
            const newStartQuarters = Math.round(mouseX / quarterWidth);
            let newStartHour = Math.floor(newStartQuarters / 4);
            let newStartMinutes = (newStartQuarters % 4) * 15;

            // 计算新的开始行（天）
            const newStartRow = Math.min(clickedRow, parseInt(endTime.day) - 1);
            let newStartDay = newStartRow + 1;

            // 确保新的开始时间不晚于结束时间
            const endHour = endTime.hours;
            const endMinutes = endTime.minutes;
            const endDay = parseInt(endTime.day);

            // 限制开始时间不能小于 00:00
            if (newStartHour < 0) {
                newStartHour = 0;
                newStartMinutes = 0;
            }

            // 确保开始日期不小于1（周一）
            if (newStartDay < 1) {
                newStartDay = 1;
            }

            // 确保开始时间和结束时间至少相差15分钟
            if (newStartDay === endDay) {
                // 同一天的情况
                const endTotalMinutes = endHour * 60 + endMinutes;
                const newStartTotalMinutes = newStartHour * 60 + newStartMinutes;

                if (endTotalMinutes - newStartTotalMinutes < 15) {
                    // 如果差距小于15分钟，将开始时间设置为结束时间前15分钟
                    const adjustedTotalMinutes = endTotalMinutes - 15;
                    if (adjustedTotalMinutes >= 0) {
                        newStartHour = Math.floor(adjustedTotalMinutes / 60);
                        newStartMinutes = adjustedTotalMinutes % 60;
                    } else {
                        // 如果结束时间小于00:15，则设置开始时间为00:00
                        newStartHour = 0;
                        newStartMinutes = 0;
                    }
                }
            }

            // 检查是否超过了结束日期
            if (newStartDay > endDay) return; // 无效的天数范围

            // 格式化新的开始时间
            const newStartDate = `${newStartDay}-${newStartHour.toString().padStart(2, '0')}:${newStartMinutes.toString().padStart(2, '0')}`;

            // 更新时间块
            const index = localTimePlan.value.findIndex(
                (plan) =>
                    plan.startDate === selectedTimePlan.value?.startDate &&
                    plan.endDate === selectedTimePlan.value?.endDate,
            );

            if (index !== -1) {
                localTimePlan.value[index].startDate = newStartDate;
                selectedTimePlan.value = localTimePlan.value[index];
            }
        } else if (dragState.value.resizeDirection === 'right') {
            // 调整结束时间
            const newEndQuarters = Math.round(mouseX / quarterWidth);
            let newEndHour = Math.floor(newEndQuarters / 4);
            let newEndMinutes = (newEndQuarters % 4) * 15;

            // 处理超过 23:59 的情况
            if (newEndHour > 23 || (newEndHour === 23 && newEndMinutes > 59)) {
                newEndHour = 23;
                newEndMinutes = 59;
            }

            // 计算新的结束行（天）
            const newEndRow = Math.max(clickedRow, parseInt(startTime.day) - 1);
            let newEndDay = newEndRow + 1;

            // 确保新的结束时间不早于开始时间
            const startHour = startTime.hours;
            const startMinutes = startTime.minutes;
            const startDay = parseInt(startTime.day);

            // 如果结束时间在当前天达到 00:00，检查是否需要跳到上一天
            if (newEndHour <= 0 && newEndMinutes <= 0) {
                // 如果不是开始天，则跳到上一天的 23:59
                if (newEndDay > startDay) {
                    // 检查鼠标是否上移了一天
                    const originalEndDay = parseInt(endTime.day);
                    if (newEndRow < originalEndDay - 1) {
                        // 如果上移了一天，则跳到上一天的 00:00
                        newEndDay--;
                        newEndHour = 0;
                        newEndMinutes = 0;
                    } else {
                        // 如果没有上移，则保持在当前天的 00:00
                        newEndHour = 0;
                        newEndMinutes = 0;
                    }
                } else {
                    // 如果已经是开始天，确保结束时间至少比开始时间晚 15 分钟
                    const startTotalMinutes = startHour * 60 + startMinutes;
                    const minEndTotalMinutes = startTotalMinutes + 15;

                    newEndHour = Math.floor(minEndTotalMinutes / 60);
                    newEndMinutes = minEndTotalMinutes % 60;

                    // 只有当鼠标位置超过最小结束时间时才允许调整
                    const mouseMinutes = newEndQuarters * 15;
                    if (mouseMinutes > minEndTotalMinutes) {
                        newEndHour = Math.floor(mouseMinutes / 60);
                        newEndMinutes = mouseMinutes % 60;
                    }
                }
            }

            // 限制结束时间不能超过 23:59
            if (newEndHour > 23 || (newEndHour === 23 && newEndMinutes > 59)) {
                newEndHour = 23;
                newEndMinutes = 59;
            }

            // 确保结束日期不大于7（周日）
            if (newEndDay > 7) {
                newEndDay = 7;
            }

            // 如果是同一天，确保开始时间和结束时间至少相差15分钟
            if (startDay === newEndDay) {
                const startTotalMinutes = startHour * 60 + startMinutes;
                const newEndTotalMinutes = newEndHour * 60 + newEndMinutes;

                if (newEndTotalMinutes - startTotalMinutes < 15) {
                    // 如果差距小于15分钟，将结束时间设置为开始时间后15分钟
                    const adjustedTotalMinutes = startTotalMinutes + 15;
                    if (adjustedTotalMinutes <= 24 * 60) {
                        newEndHour = Math.floor(adjustedTotalMinutes / 60);
                        newEndMinutes = adjustedTotalMinutes % 60;
                    } else {
                        // 如果开始时间大于23:45，则设置结束时间为23:59
                        newEndHour = 23;
                        newEndMinutes = 59;
                    }
                }
            }

            // 格式化新的结束时间
            const newEndDate = `${newEndDay}-${newEndHour.toString().padStart(2, '0')}:${newEndMinutes.toString().padStart(2, '0')}`;

            // 更新时间块
            const index = localTimePlan.value.findIndex(
                (plan) =>
                    plan.startDate === selectedTimePlan.value?.startDate &&
                    plan.endDate === selectedTimePlan.value?.endDate,
            );

            if (index !== -1) {
                localTimePlan.value[index].endDate = newEndDate;
                selectedTimePlan.value = localTimePlan.value[index];
            }

            // 更新 tip 位置
            updateTip();
            // 重绘时间块
            drawTimeBlocks();
            // 发送更新事件到父组件
            emit('timeSelect', localTimePlan.value);
            return;
        }

        // 更新 tip 位置
        updateTip();
        // 重绘时间块
        drawTimeBlocks();
        // 发送更新事件到父组件
        emit('timeSelect', localTimePlan.value);
        return;
    }

    // 处理拖拽创建新时间段的预览
    if (
        dragState.value.isDragging &&
        !dragState.value.isMovingBlock &&
        !dragState.value.isResizing
    ) {
        const moveDistance = Math.sqrt(
            Math.pow(mouseX - dragState.value.startX, 2) +
                Math.pow(mouseY - dragState.value.startY, 2),
        );

        if (moveDistance > 5) {
            dragState.value.hasMoved = true;

            const canvas = canvasRef.value;
            const timeEcharts = canvas?.closest('.time-echarts');
            if (!canvas || !timeEcharts) return;

            const canvasRect = canvas.getBoundingClientRect();
            const gridWidth = canvas.width / 24;
            const quarterWidth = gridWidth / 4;
            const rowHeight = canvas.height / 7;

            // 计算开始和结束位置
            const startPosition = Math.min(dragState.value.startX, mouseX);
            let endPosition = Math.max(dragState.value.startX, mouseX);

            // 限制结束位置不超过画布宽度（24小时）
            endPosition = Math.min(endPosition, canvas.width);

            // 计算开始和结束行（天）
            // 限制鼠标Y坐标不超过画布高度
            const limitedMouseY = Math.min(mouseY, canvas.height - 1);

            const startRow = Math.min(
                Math.floor(dragState.value.startY / rowHeight),
                Math.floor(limitedMouseY / rowHeight),
            );
            const endRow = Math.max(
                Math.floor(dragState.value.startY / rowHeight),
                Math.floor(limitedMouseY / rowHeight),
            );

            // 限制行数在有效范围内
            const limitedStartRow = Math.max(0, Math.min(startRow, 6));
            const limitedEndRow = Math.max(0, Math.min(endRow, 6));

            // 计算时间
            const startQuarters = Math.round(startPosition / quarterWidth);
            let startHour = Math.floor(startQuarters / 4);
            let startMinutes = (startQuarters % 4) * 15;

            const endQuarters = Math.round(endPosition / quarterWidth);
            let endHour = Math.floor(endQuarters / 4);
            let endMinutes = (endQuarters % 4) * 15;

            // 处理边界情况
            if (startHour < 0) {
                startHour = 0;
                startMinutes = 0;
            }

            if (endHour > 23 || (endHour === 23 && endMinutes > 59)) {
                endHour = 23;
                endMinutes = 59;
            }

            // 格式化时间
            const startDate = `${limitedStartRow + 1}-${startHour.toString().padStart(2, '0')}:${startMinutes.toString().padStart(2, '0')}`;
            const endDate = `${limitedEndRow + 1}-${endHour.toString().padStart(2, '0')}:${endMinutes.toString().padStart(2, '0')}`;

            // 更新预览 tip，使用 previewTip 相关变量
            previewTipContent.value = `${weekDays[limitedStartRow].label}-${startHour.toString().padStart(2, '0')}:${startMinutes.toString().padStart(2, '0')} - ${weekDays[limitedEndRow].label}-${endHour.toString().padStart(2, '0')}:${endMinutes.toString().padStart(2, '0')}`;

            // 计算 tip 位置
            const startTotalMinutes = limitedStartRow * 24 * 60 + startHour * 60 + startMinutes;
            const endTotalMinutes = limitedEndRow * 24 * 60 + endHour * 60 + endMinutes;
            const middleTotalMinutes = (startTotalMinutes + endTotalMinutes) / 2;

            // 将中间点转换回天和小时
            const middleDay = Math.floor(middleTotalMinutes / (24 * 60));
            const middleHourMinutes = middleTotalMinutes % (24 * 60);
            const middleHour = middleHourMinutes / 60;

            previewTipPosition.value = {
                x: canvasRect.left + (middleHour % 24) * gridWidth,
                y: canvasRect.top + middleDay * rowHeight - 10,
            };

            // 更新 tip 的 z-index 值，确保它拥有最高层级
            updateTipZIndex();

            previewTipVisible.value = true;

            // 绘制预览时间块
            drawTimeBlocks(true, { startDate, endDate });
        }
        return;
    }

    // 处理时间块的移动
    if (dragState.value.isMovingBlock && selectedTimePlan.value) {
        const currentX = e.clientX - rect.left;
        const currentY = e.clientY - rect.top;
        const deltaX = currentX - dragState.value.movingStartX;
        const deltaY = currentY - dragState.value.movingStartY;

        const gridWidth = canvas.width / 24;
        const quarterWidth = gridWidth / 4;
        const rowHeight = canvas.height / 7;

        // 检查是否是跨天时间段
        const startTime = parseTime(selectedTimePlan.value.startDate);
        const endTime = parseTime(selectedTimePlan.value.endDate);
        if (!startTime || !endTime) return;

        const isMultiDay = startTime.day !== endTime.day;

        // 计算新的垂直位置（天数）
        const newStartRow = Math.max(
            0,
            Math.min(6, Math.round(dragState.value.selectedBlockStartRow + deltaY / rowHeight)),
        );

        if (isMultiDay) {
            // 跨天时间段只能上下移动
            const dayDiff = parseInt(endTime.day) - parseInt(startTime.day);
            const newEndRow = newStartRow + dayDiff;

            // 检查是否超出边界
            if (newEndRow <= 6) {
                // 格式化新的时间
                const newStartDate = `${newStartRow + 1}-${startTime.hours.toString().padStart(2, '0')}:${startTime.minutes.toString().padStart(2, '0')}`;
                const newEndDate = `${newEndRow + 1}-${endTime.hours.toString().padStart(2, '0')}:${endTime.minutes.toString().padStart(2, '0')}`;

                // 找到当前选中时间块在数组中的索引
                const index = localTimePlan.value.findIndex(
                    (plan) =>
                        plan.startDate === selectedTimePlan.value?.startDate &&
                        plan.endDate === selectedTimePlan.value?.endDate,
                );

                if (index !== -1) {
                    // 更新本地数组中的时间块
                    localTimePlan.value[index].startDate = newStartDate;
                    localTimePlan.value[index].endDate = newEndDate;

                    // 更新选中的时间块
                    selectedTimePlan.value = localTimePlan.value[index];
                }
            }
        } else {
            // 非跨天时间段可以左右和上下移动
            // 计算时间块的持续时间（小时）
            const duration =
                endTime.hours + endTime.minutes / 60 - (startTime.hours + startTime.minutes / 60);

            // 计算新的水平位置（15分钟刻度）
            const newStartQuarters = Math.round(
                (dragState.value.selectedBlockStartX + deltaX) / quarterWidth,
            );
            let newStartHour = Math.floor(newStartQuarters / 4);
            let newStartMinutes = (newStartQuarters % 4) * 15;

            // 边界处理：如果新的开始时间小于 0，强制设为 0
            if (newStartHour < 0 || (newStartHour === 0 && newStartMinutes < 0)) {
                newStartHour = 0;
                newStartMinutes = 0;
            }

            // 计算新的结束时间
            let newEndHour = newStartHour + Math.floor(duration);
            let newEndMinutes = newStartMinutes + Math.round((duration % 1) * 60);

            // 处理分钟进位
            if (newEndMinutes >= 60) {
                newEndHour += Math.floor(newEndMinutes / 60);
                newEndMinutes = newEndMinutes % 60;
            }

            // 如果结束时间超过 23:59，调整开始时间
            if (newEndHour > 23 || (newEndHour === 23 && newEndMinutes > 59)) {
                newEndHour = 23;
                newEndMinutes = 59;
                // 反向计算新的开始时间
                newStartHour = newEndHour - Math.floor(duration);
                newStartMinutes = newEndMinutes - Math.round((duration % 1) * 60);
                if (newStartMinutes < 0) {
                    newStartHour--;
                    newStartMinutes += 60;
                }
            }

            // 格式化新的时间
            const newStartDate = `${newStartRow + 1}-${newStartHour.toString().padStart(2, '0')}:${newStartMinutes.toString().padStart(2, '0')}`;
            const newEndDate = `${newStartRow + 1}-${newEndHour.toString().padStart(2, '0')}:${newEndMinutes.toString().padStart(2, '0')}`;

            // 找到当前选中时间块在数组中的索引
            const index = localTimePlan.value.findIndex(
                (plan) =>
                    plan.startDate === selectedTimePlan.value?.startDate &&
                    plan.endDate === selectedTimePlan.value?.endDate,
            );

            if (index !== -1) {
                // 更新本地数组中的时间块
                localTimePlan.value[index].startDate = newStartDate;
                localTimePlan.value[index].endDate = newEndDate;

                // 更新选中的时间块
                selectedTimePlan.value = localTimePlan.value[index];
            }
        }

        // 更新 tip 位置
        updateTip();

        // 重绘时间块
        drawTimeBlocks();
        return;
    }

    if (!dragState.value.isDragging) return;

    // 只有当鼠标真正移动了一定距离才开始绘制预览
    const moveDistance = Math.sqrt(
        Math.pow(mouseX - dragState.value.startX, 2) + Math.pow(mouseY - dragState.value.startY, 2),
    );
    if (moveDistance > 5) {
        dragState.value.hasMoved = true;
    }

    // 只有在真正移动时才绘制预览
    if (!dragState.value.hasMoved) return;

    const currentX = e.clientX - rect.left;
    const currentY = e.clientY - rect.top;
    const gridWidth = canvas.width / 24;
    const quarterWidth = gridWidth / 4;
    const rowHeight = canvas.height / 7;

    // 清除画布并重新绘制所有时间块
    drawTimeBlocks();

    const ctx = canvas.getContext('2d');
    if (!ctx) return;

    // 计算开始和结束的行
    const startRow = Math.min(
        Math.floor(dragState.value.startY / rowHeight),
        Math.floor(currentY / rowHeight),
    );
    const endRow = Math.max(
        Math.floor(dragState.value.startY / rowHeight),
        Math.floor(currentY / rowHeight),
    );

    // 计算最近的15分钟刻度位置
    const startQuarters = Math.round(Math.min(dragState.value.startX, currentX) / quarterWidth);
    const endQuarters = Math.round(Math.max(dragState.value.startX, currentX) / quarterWidth);

    ctx.fillStyle = 'rgba(29, 185, 105, 0.2)';
    const radius = 3;
    const padding = 3;

    // 判断是否是跨天选择
    if (startRow !== endRow) {
        // 跨天逻辑：处理每一行的预览效果
        for (let row = startRow; row <= endRow; row++) {
            const y = row * rowHeight;
            let rowStartX, rowEndX;

            if (row === startRow) {
                rowStartX = startQuarters * quarterWidth;
                rowEndX = canvas.width;
            } else if (row === endRow) {
                rowStartX = 0;
                rowEndX = endQuarters * quarterWidth;
            } else {
                rowStartX = 0;
                rowEndX = canvas.width;
            }

            drawRoundedRect(
                ctx,
                rowStartX,
                y + padding,
                rowEndX - rowStartX,
                rowHeight - padding * 2,
                radius,
            );
            ctx.fill();
        }
    } else {
        // 非跨天逻辑：单个时间块
        const startX = startQuarters * quarterWidth;
        const endX = endQuarters * quarterWidth;
        const width = endX - startX;
        const y = startRow * rowHeight;

        drawRoundedRect(ctx, startX, y + padding, width, rowHeight - padding * 2, radius);
        ctx.fill();
    }

    // 更新预览 tip，使用 previewTip 相关变量
    const startHour = Math.floor(startQuarters / 4);
    const startMinutes = (startQuarters % 4) * 15;
    const endHour = Math.floor(endQuarters / 4);
    const endMinutes = (endQuarters % 4) * 15;

    const startWeekDay = startRow + 1;
    const endWeekDay = endRow + 1;

    // 显示预览 tip
    const startDayLabel = getDayLabel(startWeekDay.toString());
    const endDayLabel = getDayLabel(endWeekDay.toString());

    previewTipContent.value = `${startDayLabel}-${startHour.toString().padStart(2, '0')}:${startMinutes.toString().padStart(2, '0')} - ${endDayLabel}-${endHour.toString().padStart(2, '0')}:${endMinutes.toString().padStart(2, '0')}`;

    // 计算 tip 位置
    const tipX = ((startQuarters + endQuarters) / 2) * quarterWidth;
    const tipY = startRow * rowHeight;

    // 获取 canvas 的位置信息，包括滚动位置
    const scrollTop = window.pageYOffset || document.documentElement.scrollTop;

    // 设置 tip 位置
    previewTipPosition.value = {
        x: rect.left + tipX,
        y: rect.top + tipY - 10 + scrollTop,
    };

    // 更新 tip 的 z-index 值，确保它拥有最高层级
    updateTipZIndex();

    previewTipVisible.value = true;
};

const handleMouseUp = (e: MouseEvent) => {
    document.body.style.userSelect = '';

    // 清除预览 tip
    previewTipVisible.value = false;

    // 如果正在调整大小，不取消选中
    if (dragState.value.isResizing) {
        dragState.value.isResizing = false;
        dragState.value.resizeDirection = null;
        emit('timeSelect', localTimePlan.value);

        // 获取画布元素和位置信息
        const canvas = canvasRef.value;
        const rect = canvas?.getBoundingClientRect();

        // 调整大小结束后始终重置光标样式
        if (canvas) {
            canvas.style.cursor = 'pointer';
        }

        // 检查鼠标是否在画布外部
        const isOutsideCanvas = rect
            ? e.clientX < rect.left ||
              e.clientX > rect.right ||
              e.clientY < rect.top ||
              e.clientY > rect.bottom
            : false;

        // 只有当鼠标确实在画布外时才取消选中
        if (isOutsideCanvas) {
            resetTipPosition();
            selectedTimePlan.value = null;
            dragState.value.canMoveBlock = false;
            drawTimeBlocks();
        } else {
            // 保持选中状态
            dragState.value.canMoveBlock = true;
            dragState.value.clickedPlan = null;

            // 调整尺寸后重新计算tip位置
            if (selectedTimePlan.value) {
                updateTip();
            }
        }

        return;
    }

    // 如果正在移动时间块，不取消选中
    if (dragState.value.isMovingBlock) {
        dragState.value.isMovingBlock = false;
        emit('timeSelect', localTimePlan.value);

        // 判断鼠标是否在画布外
        const canvas = canvasRef.value;
        const rect = canvas?.getBoundingClientRect();
        if (canvas && rect) {
            // 重置光标样式为 pointer
            canvas.style.cursor = 'pointer';

            const isOutsideCanvas =
                e.clientX < rect.left ||
                e.clientX > rect.right ||
                e.clientY < rect.top ||
                e.clientY > rect.bottom;

            // 只有当鼠标确实在画布外松开时，才取消选中
            if (isOutsideCanvas) {
                resetTipPosition();
                selectedTimePlan.value = null;

                dragState.value.canMoveBlock = false;
                drawTimeBlocks();
                return;
            }
        }

        // 保持选中状态和可移动状态
        dragState.value.canMoveBlock = true;
        // 重置 clickedPlan
        dragState.value.clickedPlan = null;

        // 确保更新tip位置
        updateTip();
        return;
    }

    const canvas = canvasRef.value;
    const rect = canvas?.getBoundingClientRect();
    if (!canvas || !rect) return;

    // 检查鼠标是否在画布外部
    const isOutsideCanvas =
        e.clientX < rect.left ||
        e.clientX > rect.right ||
        e.clientY < rect.top ||
        e.clientY > rect.bottom;

    let clickX = e.clientX - rect.left;
    let clickY = e.clientY - rect.top;

    // 修正画布外的鼠标坐标
    if (isOutsideCanvas) {
        // 限制 X 坐标在画布范围内
        if (clickX < 0) clickX = 0; // 鼠标在左侧时，强制为 0（00:00）
        if (clickX > canvas.width) clickX = canvas.width; // 鼠标在右侧时，强制为最大宽度（23:59）

        // 限制 Y 坐标在画布范围内
        clickY = Math.max(0, Math.min(clickY, canvas.height - 1));
    }

    // 不使用缓存的 clickedPlan，而是直接根据当前点击位置检测
    const clickedPlan = findClickedTimePlan(clickX, clickY);

    // 如果没有移动，处理选中逻辑
    if (!dragState.value.hasMoved) {
        if (clickedPlan) {
            // 重置旧的tip位置
            resetTipPosition();

            // 选中时间块
            selectedTimePlan.value = clickedPlan;
            isSelectedBlockVisible.value = true; // 确保可见性标记为 true

            // 设置可移动状态
            dragState.value.canMoveBlock = true;

            // 立即更新tip位置和启动可见性观察
            updateTip();
            observeSelectedTimeBlock();
        } else {
            // 点击任何空白处（包括画布内外部），取消选中
            resetTipPosition();
            selectedTimePlan.value = null;
            dragState.value.canMoveBlock = false;
        }
        drawTimeBlocks();
        dragState.value.isDragging = false;
        dragState.value.clickedPlan = null;
        return;
    }

    // 如果有移动，创建新的时间段
    const gridWidth = canvas.width / 24;
    const quarterWidth = gridWidth / 4;
    const rowHeight = canvas.height / 7;

    // 限制鼠标Y坐标不超过画布高度
    const limitedClickY = Math.min(clickY, canvas.height - 1);

    // 计算开始时间
    const startPosition = Math.min(dragState.value.startX, clickX);
    const startQuarters = Math.round(startPosition / quarterWidth);
    let startHour = Math.floor(startQuarters / 4);
    let startMinutes = (startQuarters % 4) * 15;

    // 如果从右向左拖拽到画布外，将开始时间设置为 00:00
    if (isOutsideCanvas && clickX < dragState.value.startX) {
        startHour = 0;
        startMinutes = 0;
    }

    // 计算结束时间
    const endPosition = Math.max(dragState.value.startX, clickX);
    const endQuarters = Math.round(endPosition / quarterWidth);
    let endHour = Math.floor(endQuarters / 4);
    let endMinutes = (endQuarters % 4) * 15;

    // 如果从左向右拖拽到画布外，将结束时间设置为 23:59
    if (isOutsideCanvas && clickX > dragState.value.startX) {
        endHour = 23;
        endMinutes = 59;
    }

    // 限制起始Y坐标不超过画布高度
    const limitedStartY = Math.min(dragState.value.startY, canvas.height - 1);

    // 计算开始和结束的行（星期几），并限制在有效范围内
    const startRow = Math.max(
        0,
        Math.min(6, Math.floor(Math.min(limitedStartY, limitedClickY) / rowHeight)),
    );
    const endRow = Math.max(
        0,
        Math.min(6, Math.floor(Math.max(limitedStartY, limitedClickY) / rowHeight)),
    );

    // 创建跨天的时间段
    const startWeekDay = startRow + 1;
    const endWeekDay = endRow + 1;
    let startDate = `${startWeekDay}-${startHour.toString().padStart(2, '0')}:${startMinutes.toString().padStart(2, '0')}`;
    let endDate = `${endWeekDay}-${endHour.toString().padStart(2, '0')}:${endMinutes.toString().padStart(2, '0')}`;

    // 处理边界情况
    if (endHour > 23 || (endHour === 23 && endMinutes > 59)) {
        endDate = `${endWeekDay}-23:59`;
    }

    // 检查是否是有效的时间段（开始时间早于结束时间）
    const startTime = parseTime(startDate);
    const endTime = parseTime(endDate);
    if (!startTime || !endTime) return;

    // 检查如果是同一天且开始时间晚于或等于结束时间，则不创建
    if (
        startWeekDay === endWeekDay &&
        (startHour > endHour || (startHour === endHour && startMinutes >= endMinutes))
    ) {
        // 无效的时间段，不创建，但也要重置状态
        dragState.value.isDragging = false;
        dragState.value.hasMoved = false;
        dragState.value.startX = 0;
        dragState.value.startY = 0;
        dragState.value.startRow = 0;
        dragState.value.clickedPlan = null;

        // 如果之前有选中的时间块，确保其tip依然可见
        if (selectedTimePlan.value) {
            updateTip(); // 重新计算并显示选中时间块的tip
        }

        drawTimeBlocks();
        return;
    }

    // 添加新的时间段到本地数组
    const newPlan = { startDate, endDate };
    localTimePlan.value.push(newPlan);

    // 发送事件到父组件
    emit('timeSelect', localTimePlan.value);

    // 重置拖拽状态
    dragState.value.isDragging = false;
    dragState.value.hasMoved = false;
    dragState.value.startX = 0;
    dragState.value.startY = 0;
    dragState.value.startRow = 0;
    dragState.value.clickedPlan = null;

    // 如果有选中的时间块，更新其tip
    if (selectedTimePlan.value) {
        updateTip(); // 确保选中的时间块tip保持可见
    }

    drawTimeBlocks();
};

// 修改 handleKeyDown 函数，添加删除选中时间块的功能
const handleKeyDown = (e: KeyboardEvent) => {
    // 如果有选中的时间块，并且按下了 Backspace 或 Delete 键
    if (selectedTimePlan.value && (e.key === 'Backspace' || e.key === 'Delete')) {
        // 确保删除后至少还有一条数据
        if (localTimePlan.value.length > 1) {
            // 找到选中时间块在数组中的索引，移除对不存在的day属性的引用
            const index = localTimePlan.value.findIndex(
                (plan) =>
                    plan.startDate === selectedTimePlan.value?.startDate &&
                    plan.endDate === selectedTimePlan.value?.endDate,
            );

            // 如果找到了，从数组中删除
            if (index !== -1) {
                // 清除选中状态 - 在删除前就清除引用，避免引用已删除对象
                selectedTimePlan.value = null;
                dragState.value.canMoveBlock = false;

                // 删除时间块
                localTimePlan.value.splice(index, 1);

                // 重新绘制时间块
                drawTimeBlocks();

                // 发送更新事件到父组件
                emit('timeSelect', localTimePlan.value);
            }
        }

        // 阻止默认行为（如浏览器的后退）
        e.preventDefault();
    }
};

// 计算属性
const blockHeight = computed(() =>
    smallestUnitWeek.value > MAX_BLOCK_HEIGHT ? MAX_BLOCK_HEIGHT : smallestUnitWeek.value,
);

const weekdayStyle = computed(() => ({
    height: `${blockHeight.value}px`,
    lineHeight: `${blockHeight.value}px`,
}));

const rightWeekStyle = computed(() => ({
    backgroundSize: `${smallestUnitWeek.value}px ${blockHeight.value}px`,
    height: `${blockHeight.value * 7}px`,
}));

const validTimePlans = computed(() =>
    localTimePlan.value.filter((plan) => plan.startDate && plan.endDate),
);

// 工具函数
const calculateCellWidth = () => {
    if (!rightWeekRef.value) return;
    const rightDayWidth = rightWeekRef.value.clientWidth;
    smallestUnitWeek.value = parseFloat((rightDayWidth / HOURS_IN_DAY).toFixed(2));
    timeToPixels.value = smallestUnitWeek.value / 4;
};

const isValidDateString = (dateStr: string): boolean => {
    if (!dateStr) return false;
    const [day, time] = dateStr.split('-');
    if (!day || !time) return false;
    const [hours, minutes] = time.split(':');
    return !isNaN(Number(day)) && !isNaN(Number(hours)) && !isNaN(Number(minutes));
};

const isValidEntry = (entry: { startDate: string; endDate?: string }): boolean => {
    return Boolean(
        entry.startDate &&
            entry.endDate &&
            isValidDateString(entry.startDate) &&
            isValidDateString(entry.endDate),
    );
};

const parseTime = (timeStr?: string): { day: string; hours: number; minutes: number } | null => {
    if (!timeStr) return null;
    try {
        // 对于周计划，时间格式为 "day-hours:minutes"
        const [dayPart, timePart] = timeStr.split('-');
        if (!dayPart || !timePart) return null;

        const [hoursPart, minutesPart] = timePart.split(':');
        if (!hoursPart || !minutesPart) return null;

        const day = dayPart.trim();
        const hours = parseInt(hoursPart.trim(), 10);
        const minutes = parseInt(minutesPart.trim(), 10);

        // 确保解析结果是有效的数字
        if (isNaN(hours) || isNaN(minutes)) return null;

        return { day, hours, minutes };
    } catch (error) {
        console.error('Error parsing time:', error);
        return null;
    }
};

const calculateX = (startDate: string): number => {
    const time = parseTime(startDate);
    if (!time) return 0;
    return ((time.hours * MINUTES_PER_HOUR + time.minutes) / MINUTES_PER_GRID) * timeToPixels.value;
};

const calculateY = (startDate: string): number => {
    const time = parseTime(startDate);
    if (!time) return 0;
    const dayIndex = weekDays.findIndex((d) => d.value === time.day);
    return dayIndex * blockHeight.value;
};

const calculateWidth = (startDate: string, endDate: string): number => {
    const start = parseTime(startDate);
    const end = parseTime(endDate);
    if (!start || !end) return 0;

    const startMinutes = start.hours * MINUTES_PER_HOUR + start.minutes;
    const endMinutes = end.hours * MINUTES_PER_HOUR + end.minutes;
    return ((endMinutes - startMinutes) / MINUTES_PER_GRID) * timeToPixels.value;
};

const calculateSegments = (startDate: string, endDate: string): TimeSegment[] => {
    const start = parseTime(startDate);
    const end = parseTime(endDate);
    if (!start || !end) return [];

    const segments: TimeSegment[] = [];
    const startDayIndex = weekDays.findIndex((d) => d.value === start.day);
    const endDayIndex = weekDays.findIndex((d) => d.value === end.day);

    if (startDayIndex === endDayIndex) {
        segments.push({
            x: calculateX(startDate),
            y: calculateY(startDate),
            width: calculateWidth(startDate, endDate),
        });
    } else {
        // 第一天剩余时间
        const minutesTillMidnight =
            MINUTES_PER_HOUR * HOURS_IN_DAY - (start.hours * MINUTES_PER_HOUR + start.minutes);
        segments.push({
            x: calculateX(startDate),
            y: calculateY(startDate),
            width: (minutesTillMidnight / MINUTES_PER_GRID) * timeToPixels.value,
        });

        // 中间完整天数
        for (let i = startDayIndex + 1; i < endDayIndex; i++) {
            segments.push({
                x: 0,
                y: i * blockHeight.value,
                width: HOURS_IN_DAY * 4 * timeToPixels.value,
            });
        }

        // 最后一天
        segments.push({
            x: 0,
            y: endDayIndex * blockHeight.value,
            width:
                ((end.hours * MINUTES_PER_HOUR + end.minutes) / MINUTES_PER_GRID) *
                timeToPixels.value,
        });
    }

    return segments;
};

const drawTimeBlocks = (
    isPreview: boolean = false,
    plan?: { startDate: string; endDate: string },
) => {
    const container = rightWeekRef.value;
    const gridContainer = container?.querySelector('.grid-container');
    const canvas = canvasRef.value;
    if (!container || !gridContainer || !canvas) return;

    const containerWidth = gridContainer.clientWidth;
    const containerHeight = container.clientHeight;

    // 设置canvas尺寸
    canvas.width = containerWidth;
    canvas.height = containerHeight;

    const ctx = canvas.getContext('2d');
    if (!ctx) return;

    // 清除之前的绘制内容
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // 分离选中的时间块和非选中的时间块
    const selectedPlan = selectedTimePlan.value;
    const nonSelectedPlans = validTimePlans.value.filter(
        (entry) =>
            !selectedPlan ||
            entry.startDate !== selectedPlan.startDate ||
            entry.endDate !== selectedPlan.endDate,
    );

    // 先绘制所有非选中的时间块
    nonSelectedPlans.forEach((entry) => {
        if (!isValidEntry(entry)) return;

        const segments = calculateSegments(entry.startDate, entry.endDate);
        segments.forEach((segment) => {
            // 绘制时间块
            ctx.fillStyle = 'rgba(29, 185, 105, 0.3)';
            ctx.beginPath();

            // 绘制圆角矩形
            const radius = 3;
            const padding = 3;

            ctx.moveTo(segment.x + radius, segment.y + padding);
            ctx.lineTo(segment.x + segment.width - radius, segment.y + padding);
            ctx.quadraticCurveTo(
                segment.x + segment.width,
                segment.y + padding,
                segment.x + segment.width,
                segment.y + padding + radius,
            );
            ctx.lineTo(segment.x + segment.width, segment.y + blockHeight.value - padding - radius);
            ctx.quadraticCurveTo(
                segment.x + segment.width,
                segment.y + blockHeight.value - padding,
                segment.x + segment.width - radius,
                segment.y + blockHeight.value - padding,
            );
            ctx.lineTo(segment.x + radius, segment.y + blockHeight.value - padding);
            ctx.quadraticCurveTo(
                segment.x,
                segment.y + blockHeight.value - padding,
                segment.x,
                segment.y + blockHeight.value - padding - radius,
            );
            ctx.lineTo(segment.x, segment.y + padding + radius);
            ctx.quadraticCurveTo(
                segment.x,
                segment.y + padding,
                segment.x + radius,
                segment.y + padding,
            );

            ctx.fill();
        });
    });

    // 最后绘制选中的时间块（如果有）
    if (selectedPlan && isValidEntry(selectedPlan)) {
        const segments = calculateSegments(selectedPlan.startDate, selectedPlan.endDate);
        segments.forEach((segment) => {
            // 添加高亮效果
            ctx.save();
            ctx.shadowColor = 'rgba(117, 211, 186, 0.6)';
            ctx.shadowBlur = 3;
            ctx.shadowOffsetX = 0;
            ctx.shadowOffsetY = 0;

            // 使用稍微不同的颜色突出显示选中的时间块
            ctx.fillStyle = 'rgba(29, 185, 105, 0.4)';
            ctx.beginPath();

            // 绘制圆角矩形
            const radius = 3;
            const padding = 3;

            ctx.moveTo(segment.x + radius, segment.y + padding);
            ctx.lineTo(segment.x + segment.width - radius, segment.y + padding);
            ctx.quadraticCurveTo(
                segment.x + segment.width,
                segment.y + padding,
                segment.x + segment.width,
                segment.y + padding + radius,
            );
            ctx.lineTo(segment.x + segment.width, segment.y + blockHeight.value - padding - radius);
            ctx.quadraticCurveTo(
                segment.x + segment.width,
                segment.y + blockHeight.value - padding,
                segment.x + segment.width - radius,
                segment.y + blockHeight.value - padding,
            );
            ctx.lineTo(segment.x + radius, segment.y + blockHeight.value - padding);
            ctx.quadraticCurveTo(
                segment.x,
                segment.y + blockHeight.value - padding,
                segment.x,
                segment.y + blockHeight.value - padding - radius,
            );
            ctx.lineTo(segment.x, segment.y + padding + radius);
            ctx.quadraticCurveTo(
                segment.x,
                segment.y + padding,
                segment.x + radius,
                segment.y + padding,
            );

            ctx.fill();

            // 绘制边框
            ctx.strokeStyle = '#75d3ba';
            ctx.lineWidth = 1.2;
            ctx.stroke();
            ctx.restore();
        });
    }

    // 如果是预览模式，再绘制预览时间块
    if (isPreview && plan) {
        const segments = calculateSegments(plan.startDate, plan.endDate);
        segments.forEach((segment) => {
            // 使用不同的颜色或透明度来区分预览时间块
            ctx.fillStyle = 'rgba(29, 185, 105, 0.2)'; // 使用更透明的颜色
            ctx.beginPath();

            // 绘制圆角矩形
            const radius = 3;
            const padding = 3;

            ctx.moveTo(segment.x + radius, segment.y + padding);
            ctx.lineTo(segment.x + segment.width - radius, segment.y + padding);
            ctx.quadraticCurveTo(
                segment.x + segment.width,
                segment.y + padding,
                segment.x + segment.width,
                segment.y + padding + radius,
            );
            ctx.lineTo(segment.x + segment.width, segment.y + blockHeight.value - padding - radius);
            ctx.quadraticCurveTo(
                segment.x + segment.width,
                segment.y + blockHeight.value - padding,
                segment.x + segment.width - radius,
                segment.y + blockHeight.value - padding,
            );
            ctx.lineTo(segment.x + radius, segment.y + blockHeight.value - padding);
            ctx.quadraticCurveTo(
                segment.x,
                segment.y + blockHeight.value - padding,
                segment.x,
                segment.y + blockHeight.value - padding - radius,
            );
            ctx.lineTo(segment.x, segment.y + padding + radius);
            ctx.quadraticCurveTo(
                segment.x,
                segment.y + padding,
                segment.x + radius,
                segment.y + padding,
            );

            ctx.fill();

            // 添加虚线边框以区分预览
            ctx.strokeStyle = 'rgba(29, 185, 105, 0.5)';
            ctx.lineWidth = 1;
            ctx.setLineDash([3, 3]); // 设置虚线样式
            ctx.stroke();
            ctx.setLineDash([]); // 重置虚线样式
        });
    }
};

// 添加一个处理document点击事件的函数，用于在点击非时间块区域时取消选中
const handleDocumentClick = (e: MouseEvent) => {
    // 如果当前没有选中时间块，或者正在调整大小或移动，不做处理
    if (
        !selectedTimePlan.value ||
        dragState.value.isResizing ||
        dragState.value.isMovingBlock ||
        dragState.value.isDragging
    ) {
        return;
    }

    const canvas = canvasRef.value;
    if (!canvas) return;

    // 检查点击是否发生在画布上
    const rect = canvas.getBoundingClientRect();
    const isClickOnCanvas =
        e.clientX >= rect.left &&
        e.clientX <= rect.right &&
        e.clientY >= rect.top &&
        e.clientY <= rect.bottom;

    if (isClickOnCanvas) {
        // 如果点击在画布上，我们需要检查是否点击在时间块上
        const clickX = e.clientX - rect.left;
        const clickY = e.clientY - rect.top;
        const clickedPlan = findClickedTimePlan(clickX, clickY);

        // 如果点击的不是当前选中的时间块，则取消选中
        if (
            !clickedPlan ||
            clickedPlan.startDate !== selectedTimePlan.value.startDate ||
            clickedPlan.endDate !== selectedTimePlan.value.endDate
        ) {
            resetTipPosition();
            selectedTimePlan.value = null;
            dragState.value.canMoveBlock = false;
            drawTimeBlocks();
        }

        return;
    }

    // 点击在画布外，取消选中
    resetTipPosition();
    selectedTimePlan.value = null;
    dragState.value.canMoveBlock = false;
    drawTimeBlocks();
};

// 生命周期
onMounted(() => {
    if (!Array.isArray(props.timePlan)) {
        smallestUnitWeek.value = 0;
        return;
    }

    // 初始化 tip 的 z-index 值
    updateTipZIndex();

    calculateCellWidth();

    const resizeObserver = new ResizeObserver(() => {
        calculateCellWidth();
        drawTimeBlocks();
        // 在容器大小变化时更新 tip 位置
        updateTip();
        // 重新检查选中时间块的可见性
        observeSelectedTimeBlock();
    });

    if (rightWeekRef.value) {
        resizeObserver.observe(rightWeekRef.value);
        // 同时监听整个窗口的大小变化
        window.addEventListener('resize', updateTip);

        const canvas = canvasRef.value;
        if (canvas) {
            canvas.addEventListener('mousedown', handleMouseDown);
            document.addEventListener('mousemove', handleMouseMove);
            document.addEventListener('mouseup', handleMouseUp);
            document.addEventListener('keydown', handleKeyDown);
            // 添加 document 点击事件处理
            document.addEventListener('click', handleDocumentClick);
        }
    }

    // 添加滚动事件监听 - 使用 passive: true 优化滚动性能
    window.addEventListener('scroll', handleScroll, { passive: true, capture: true });

    // 尝试检测可用的传送门组件
    if (typeof window !== 'undefined') {
        if (!isVue2) {
            // Vue 3 环境
            try {
                // 动态获取 Teleport
                const Vue = require('vue');
                if (Vue.Teleport) {
                    portalComponent.value = 'Teleport';
                    hasPortalSupport.value = true;
                }
            } catch (e) {
                console.error('Failed to import Teleport:', e);
            }
        } else {
            // Vue 2 环境 - 尝试使用 portal-vue
            try {
                // 检查是否安装了 portal-vue
                if (window.Vue && window.Vue.component && window.Vue.component('Portal')) {
                    portalComponent.value = 'portal';
                    hasPortalSupport.value = true;
                }
            } catch (e) {
                console.error('Failed to detect Portal:', e);
            }
        }
    }

    onBeforeUnmount(() => {
        // 清理 ResizeObserver
        if (resizeObserver) {
            resizeObserver.disconnect();
        }

        // 清理事件监听
        document.removeEventListener('mousemove', handleMouseMove);
        document.removeEventListener('mouseup', handleMouseUp);
        document.removeEventListener('keydown', handleKeyDown);
        window.removeEventListener('resize', updateTip);
        window.removeEventListener('scroll', handleScroll, {
            capture: true,
        } as EventListenerOptions);
        document.removeEventListener('click', handleDocumentClick);

        // 清理 IntersectionObserver
        if (timeBlockObserver) {
            timeBlockObserver.disconnect();
            timeBlockObserver = null;
        }

        // 清理滚动定时器
        if (scrollTimerId) {
            clearTimeout(scrollTimerId);
            scrollTimerId = null;
        }

        // 清理可能残留的追踪元素
        cleanupTrackingElements();

        // 调用所有注册的清理函数
        trackingCleanupFunctions.value.forEach((cleanup) => cleanup());
        // 清空数组
        trackingCleanupFunctions.value = [];

        const canvas = canvasRef.value;
        if (canvas) {
            canvas.removeEventListener('mousedown', handleMouseDown);
        }
    });
});

// 添加一个清理追踪元素的函数
const cleanupTrackingElements = () => {
    if (trackingCleanupFunctions.value.length > 0) {
        trackingCleanupFunctions.value.forEach((cleanup) => {
            try {
                cleanup();
            } catch (error) {
                console.error('Error running cleanup function:', error);
            }
        });
        trackingCleanupFunctions.value = [];
    }

    // 额外的安全措施，移除所有可能残留的追踪元素
    try {
        const container = canvasRef.value?.parentElement;
        if (container) {
            const trackers = container.querySelectorAll('[data-time-block-tracker]');
            trackers.forEach((tracker) => {
                if (tracker.parentElement) {
                    tracker.parentElement.removeChild(tracker);
                }
            });
        }
    } catch (error) {
        console.error('Error removing tracking elements:', error);
    }
};

// 修改 watch 函数，确保选中状态变化时正确处理tip位置
watch(
    () => selectedTimePlan.value,
    (newVal, oldVal) => {
        if (newVal) {
            // 如果是新选中的时间块，立即更新观察者和tip位置
            requestAnimationFrame(() => {
                updateTip();
                observeSelectedTimeBlock();
            });
        } else if (oldVal && !newVal) {
            // 如果是取消选中，重置tip位置
            resetTipPosition();
        }
    },
    { deep: true },
);
</script>

<style scoped lang="less">
.time-echarts {
    @border-color: #cccccc;
    position: relative;
    display: flex;
    justify-content: center;
    align-items: center;
    margin-bottom: 20px;

    .contents-week {
        width: 100%;
        margin-bottom: 20px;
    }

    .ul-week {
        display: flex;
        margin-left: 40px;
        flex-grow: 1;
        flex-shrink: 0;
        padding: 0;
        li {
            flex: 1;
            height: 24px;
            text-align: center;
            line-height: 23px;

            box-sizing: border-box;
            font-size: 11px;
            list-style: none;
            padding: 0;
            color: #7e8494;
        }
    }

    .weekday-week {
        font-size: 11px;
        background-color: #fff;
        width: 40px;
        text-align: center;
        position: relative;
        color: #7e8494;
    }

    .bottom {
        display: flex;
    }

    .rightweek {
        width: 100%;
        position: relative;
        background-color: #fff;
        border-top: 1px solid @border-color;
        box-sizing: border-box;

        .grid-container {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            display: grid;
            grid-template-columns: repeat(24, 1fr);
            grid-template-rows: repeat(7, 1fr);
            pointer-events: none;
            border-left: 1px solid @border-color;

            .grid-column {
                border-right: 1px solid #cccccc;
                border-bottom: 1px solid #cccccc;
            }
        }

        canvas {
            position: absolute;
            top: 0;
            left: 0;
            height: 100%;
            width: 100%;
            pointer-events: auto;
        }
    }

    .tip-container {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        pointer-events: none;
        /* z-index is now set dynamically */
    }

    .time-tip {
        position: absolute; // 改为 absolute
        background: linear-gradient(to bottom, #2c3e50, #34495e);
        color: white;
        padding: 5px 10px;
        line-height: normal;

        border-radius: 4px;
        font-size: 12px;
        pointer-events: none;
        white-space: nowrap;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
        letter-spacing: 0.5px;
        font-weight: 500;
        border: 1px solid rgba(255, 255, 255, 0.1);

        &::after {
            content: '';
            position: absolute;
            left: 50%;
            bottom: -5px;
            transform: translateX(-50%);
            border-left: 5px solid transparent;
            border-right: 5px solid transparent;
            border-top: 5px solid #34495e;
            filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.1));
        }

        animation: tipFadeIn 0.2s ease-out;

        &.preview-tip {
            background: linear-gradient(to bottom, #3a5273, #4a6284);
        }
    }

    @keyframes tipFadeIn {
        from {
            opacity: 0;
            transform: translate(-50%, -90%);
        }
        to {
            opacity: 1;
            transform: translate(-50%, -100%);
        }
    }
}
</style>
